package server.core;

import server.*;
import server.loader.WebappLoader;
import server.session.StandardManager;
import server.util.RequestUtil;
import server.xml.FilterDef;
import server.xml.FilterMap;

import javax.servlet.*;
import java.util.*;


public class StandardContext extends ContainerBase implements Context {

    private Wrapper staticResourcesWrapper = null;

    public void setStaticResourcesWrapper(Wrapper wrapper) {
        staticResourcesWrapper = wrapper;
    }

    public Wrapper getStaticResourcesWrapper() {
        return staticResourcesWrapper;
    }
    //不完整
    //实现LifeCycle接口---------------------------------------------------------------------------
    @Override
    public synchronized void start() throws LifecycleException {
        if (started)
            throw new LifecycleException("容器 {" + logName() + "} 已经启动过");
        if (debug >= 1)
            log("启动");
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

        setAvailable(false);
        setConfigured(false);

        if (getLoader() == null)
            setLoader(new WebappLoader());

        if (getManager() == null)
            setManager(new StandardManager());

        boolean ok = true;
        if (ok) {
            addDefaultMapper(mapperClass);
            started = true;
            if (loader != null && loader instanceof Lifecycle)
                ((Lifecycle) loader).start();
            if (logger != null && logger instanceof Lifecycle)
                ((Lifecycle) logger).start();
            for (Mapper mapper : findMappers()) {
                if (mapper instanceof Lifecycle)
                    ((Lifecycle) mapper).start();
            }
            for (Container container : findChildren()) {
                if (container instanceof Lifecycle)
                    ((Lifecycle) container).start();
            }
            if (pipeline instanceof Lifecycle)
                ((Lifecycle) pipeline).start();
            //触发启动事件,这时会有相应的监听器完成配置工作
            lifecycle.fireLifecycleEvent(START_EVENT, null);
            if ((manager != null) && (manager instanceof Lifecycle))
                ((Lifecycle) manager).start();
        }
        //监听器配置失败
        if (!getConfigured())
            ok = false;
        //启动监听器
        if (ok) {
            if (!listenerStart())
                ok = false;
        }
        //启动过滤器
        if (ok) {
            if (!filterStart())
                ok = false;
        }
        if (ok) {
            if (debug >= 1)
                log("Starting completed");
            setAvailable(true);
        } else {
            log("启动应用[" + getName() + "]失败");
            try {
                stop();
            } catch (Throwable t) {
                log("Exception during cleanup after start failed");
            }
            setAvailable(false);
            return;
        }
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
        log("启动应用[" + getName() + "]成功");
    }

    @Override
    public synchronized void stop() throws LifecycleException {
        if (!started)
            throw new LifecycleException("容器 {" + logName() + "} 没有启动过");
        if (debug >= 1)
            log("Stopping");
        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
        setAvailable(false);
        filterStop();
        if ((manager != null) && (manager instanceof Lifecycle)) {
            ((Lifecycle) manager).stop();
        }
        if (debug >= 1)
            log("Processing standard container shutdown");
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;
        if (pipeline instanceof Lifecycle) {
            ((Lifecycle) pipeline).stop();
        }
        for (Container container : findChildren()) {
            if (container instanceof Lifecycle)
                ((Lifecycle) container).stop();
        }
        for (Mapper mapper : findMappers()) {
            if (mapper instanceof Lifecycle)
                ((Lifecycle) mapper).stop();
        }
        listenerStop();
        if ((logger != null) && (logger instanceof Lifecycle)) {
            ((Lifecycle) logger).stop();
        }
        if ((loader != null) && (loader instanceof Lifecycle)) {
            ((Lifecycle) loader).stop();
        }
        context = null;
        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

        if (debug >= 1)
            log("Stopping complete");

        log("关闭应用[" + getName() + "]成功");
    }
    //重新加载
    @Override
    public void reload() {
        if (!started)
            throw new IllegalStateException("容器 {" + logName() + "} 没有启动过");
        log("重新加载应用[" + getName() + "]中");
        if ((manager != null) && (manager instanceof Lifecycle)) {
            try {
                ((Lifecycle) manager).stop();
            } catch (LifecycleException e) {
                log("暂停Session管理器失败", e);
            }
        }
        for (Container container : findChildren()) {
            Wrapper wrapper = (Wrapper) container;
            if (wrapper instanceof Lifecycle)
                try {
                    ((Lifecycle) wrapper).stop();
                } catch (LifecycleException e) {
                    log("Exception stopping Wrapper for servlet {" + wrapper.getName() + "}");
                }
        }
        listenerStop();
        filterStop();
        if (loader != null && (loader instanceof Lifecycle)) {
            try {
                ((Lifecycle) loader).stop();
            } catch (LifecycleException e) {
                log("Exception stopping Loader", e);
            }
        }
        //重新启动
        if ((loader != null) && (loader instanceof Lifecycle)) {
            try {
                ((Lifecycle) loader).start();
            } catch (LifecycleException e) {
                log("Exception starting Loader", e);
            }
        }

        boolean ok = true;
        if (ok) {
            if (!listenerStart()) {
                log("启动监听器失败");
                ok = false;
            }
        }
        if (ok) {
            if (!filterStart()) {
                log("启动过滤器失败");
                ok = false;
            }
        }
        for (Container container : findChildren()) {
            if (!ok)
                break;
            Wrapper wrapper = (Wrapper) container;
            if (wrapper instanceof Lifecycle) {
                try {
                    ((Lifecycle) wrapper).start();
                } catch (LifecycleException e) {
                    log("Exception starting Wrapper for servlet {" + wrapper.getName() + "}");
                    ok = false;
                }
            }
        }
        if ((manager != null) && (manager instanceof Lifecycle)) {
            try {
                ((Lifecycle) manager).start();
            } catch (LifecycleException e) {
                log("启动Session管理器失败", e);
            }
        }
        if (ok) {
            log("重新加载应用[" + getName() + "]成功");
        } else {
            setAvailable(false);
            log("重新加载应用[" + getName() + "]失败");
        }
    }

    public StandardContext() {
        pipeline.setBasic(new StandardContextValve());
    }
    //属性----------------------------------------------------------------------------
    //应用是否可用
    private boolean available = false;
    //是否配置成功
    private boolean configured = false;
    //应用信息
    private static final String info = "server.core.StandardContext/1.0";
    //匹配路径与servlet名之间的映射
    private HashMap<String, String> servletMappings = new HashMap<>();
    private HashMap<String, FilterConfig> filterConfigs = new HashMap<>();
    //存放过滤器名与拦截路径及拦截Servlet名之间的映射关系
    private FilterMap[] filterMaps = new FilterMap[0];
    //存放过滤器定义信息(以过滤器名为键)
    private HashMap<String, FilterDef> filterDefs = new HashMap<>();
    //默认的映射器全类名
    private String mapperClass = "server.core.StandardContextMapper";
    private ApplicationContext context = null;
    //是否支持重加载
    private boolean reloadable = false;
    //应用监听器全类名集合
    private String applicationListeners[] = new String[0];
    //监听器对象集合
    private Object applicationListenersObjects[] = new Object[0];
    //Session的超时时间 单位为分钟
    private int sessionTimeout = 30;
    //存放错误码与转发地址的关系 key:错误码 value:转发地址
    private Map<Integer, String> errorPages = new HashMap<>();
    //欢迎文件
    private String[] welcomeFileList = new String[0];


    public FilterConfig findFilterConfig(String name) {
        synchronized (filterConfigs) {
            return filterConfigs.get(name);
        }
    }


    //校正匹配模式,除*.xxx格式外的路径必须以/开头
    private String adjustURLPattern(String pattern) {
        if (pattern == null || pattern.startsWith("/") || pattern.startsWith("*."))
            return pattern;
        log("WARNING: URL pattern {" + pattern + "} must start with a '/'");
        return "/" + pattern;
    }

    //验证匹配路径是否符合规则,符合返回true,否则返回false
    private boolean validateURLPattern(String pattern) {
        if (pattern == null)
            return false;
        if (pattern.startsWith("*.")) {
            //后缀中不能有/
            if (pattern.indexOf('/') < 0)
                return true;
            return false;
        }
        //除后缀匹配外,匹配路径都必须以/开头
        if (pattern.startsWith("/"))
            return true;
        return false;
    }

    //停止过滤器
    public void filterStop() {
        if (debug >= 1)
            log("Stopping filters");

        synchronized (filterConfigs) {
            for (FilterConfig filterConfig : filterConfigs.values()) {
                if (debug >= 1)
                    log(" Stopping filter '" + filterConfig.getFilterName() + "'");
                ApplicationFilterConfig afc = (ApplicationFilterConfig)filterConfig;
                afc.release();
            }
            filterConfigs.clear();
        }
    }

    public boolean filterStart() {
        if (debug >= 1)
            log("Starting filters");
        boolean ok = true;
        synchronized (filterConfigs) {
            filterConfigs.clear();
            for (FilterDef filterDef : filterDefs.values()) {
                if (debug >= 1)
                    log("启动过滤器[" + filterDef.getFilterName() + "]");
                ApplicationFilterConfig filterConfig = null;
                try {
                    filterConfig = new ApplicationFilterConfig(this, filterDef);
                    filterConfigs.put(filterConfig.getFilterName(), filterConfig);
                } catch (Throwable t) {
                    log("启动过滤器[" + filterDef.getFilterName() + "]失败", t);
                    ok = false;
                }
            }
        }
        return ok;
    }

    public boolean listenerStart() {
        if (debug >= 1)
            log("正在配置应用监听器");
        ClassLoader loader = getLoader().getClassLoader();
        String[] listeners = findApplicationListeners();
        Object[] instances = new Object[listeners.length];
        boolean ok = true;
        //实例
        for (int i = 0; i < listeners.length; i++) {
            try {
                Class clazz = loader.loadClass(listeners[i]);
                instances[i] = clazz.newInstance();
            } catch (Throwable t) {
                log("配置监听器{" + listeners[i] + "}错误", t);
                ok = false;
                return false;
            }
        }
        setApplicationListeners(instances);

        ServletContextEvent event = new ServletContextEvent(getServletContext());
        for (int i = 0; i < instances.length; i++) {
            if (instances[i] == null)
                continue;
            if (!(instances[i] instanceof ServletContextListener))
                continue;
            ServletContextListener listener =
                    (ServletContextListener) instances[i];
            try {
                //触发Context初始化事件
                listener.contextInitialized(event);
            } catch (Throwable t) {
                log("Context初始化事件监听器出错", t);
                ok = false;
            }
        }
        return ok;
    }

    public boolean listenerStop() {
        boolean ok = true;
        Object listeners[] = getApplicationListeners();
        if (listeners == null)
            return ok;
        ServletContextEvent event = new ServletContextEvent(getServletContext());
        for (int i = listeners.length - 1 ; i >= 0 ; i--) {
            if(listeners[i] == null)
                continue;
            if(!(listeners[i] instanceof ServletContextListener))
            continue;
            ServletContextListener listener = (ServletContextListener) listeners[i];
            try {
                listener.contextDestroyed(event);
            } catch (Throwable t) {
                log("Context销毁事件监听器出错", t);
                ok = false;
            }
        }
        setApplicationListeners(null);
        return ok;
    }
    //getter---------------------------------------------------------------------------

    //setter---------------------------------------------------------------------------

    //实现Container接口---------------------------------------------------------------------------
    @Override
    public String getInfo() {
        return info;
    }

    @Override
    public void addChild(Container child) {
        if (!(child instanceof Wrapper))
            throw new IllegalArgumentException("Child of a Context must be a Wrapper");
        super.addChild(child);
    }

    //实现Context接口---------------------------------------------------------------------------


    @Override
    public void addErrorPage(int statusCode, String location) {
        errorPages.put(statusCode, location);
    }

    @Override
    public String[] findWelcomeFiles() {
        return welcomeFileList;
    }

    @Override
    public void addWelcomeFile(String name) {
        synchronized (welcomeFileList) {
            String[] results = new String[welcomeFileList.length + 1];
            for (int i = 0; i < welcomeFileList.length; i++)
                results[i] = welcomeFileList[i];
            results[welcomeFileList.length] = name;
            welcomeFileList = results;
        }
    }



    @Override
    public String findErrorPage(int status) {
        return errorPages.get(status);
    }

    @Override
    public ServletContext getServletContext() {
        if (context == null)
            context = new ApplicationContext(getName(), this);
        return context;
    }

    @Override
    public int getSessionTimeout() {
        return sessionTimeout;
    }

    @Override
    public void setSessionTimeout(int sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    @Override
    public void addApplicationListener(String listener) {
        synchronized (applicationListeners) {
            String results[] =new String[applicationListeners.length + 1];
            for (int i = 0; i < applicationListeners.length; i++) {
                if (listener.equals(applicationListeners[i]))
                    return;
                results[i] = applicationListeners[i];
            }
            results[applicationListeners.length] = listener;
            applicationListeners = results;
        }
    }

    public Object[] getApplicationListeners() {
        return applicationListenersObjects;
    }

    @Override
    public void setApplicationListeners(Object[] listeners) {
        applicationListenersObjects = listeners;
    }

    @Override
    public String[] findApplicationListeners() {
        return applicationListeners;
    }

    @Override
    public void removeFilterMap(FilterMap filterMap) {
        int index = -1;
        synchronized (filterMaps) {
            for (int i = 0; i < filterMaps.length; i++) {
                if (filterMaps[i] == filterMap) {
                    index = i;
                    break;
                }
            }
            if (index == -1)
                return;
            FilterMap[] newArr = new FilterMap[filterMaps.length - 1];
            int n = 0;
            for (int i = 0; i < filterMaps.length; i++) {
                if(i != index)
                    newArr[n++] = filterMaps[i];
            }
            filterMaps = newArr;
        }
    }

    @Override
    public void addFilterMap(FilterMap filterMap) {
        String filterName = filterMap.getFilterName();
        String servletName = filterMap.getServletName();
        String urlPattern = filterMap.getUrlPattern();
        if (findFilterDef(filterName) == null)
            throw new IllegalArgumentException("过滤器{" + filterName + "}未定义");
        if (servletName == null && urlPattern == null)
            throw new IllegalArgumentException("过滤器映射必须指定<url-pattern> 或 <servlet-name>");
        if (urlPattern != null && !validateURLPattern(urlPattern))
            throw new IllegalArgumentException("无效的匹配路径{" + urlPattern + "}");
        synchronized (filterMaps) {
            FilterMap[] newArr = new FilterMap[filterMaps.length + 1];
            System.arraycopy(filterMaps, 0, newArr, 0, filterMaps.length);
            newArr[filterMaps.length] = filterMap;
            filterMaps = newArr;
        }
    }
    @Override
    public FilterMap[] findFilterMaps() {
        return filterMaps;
    }


    @Override
    public void addFilterDef(FilterDef filterDef) {
        synchronized (filterDefs) {
            filterDefs.put(filterDef.getFilterName(), filterDef);
        }
    }

    @Override
    public FilterDef findFilterDef(String filterName) {
        synchronized (filterDefs) {
            return filterDefs.get(filterName);
        }
    }

    @Override
    public FilterDef[] findFilterDefs() {
        synchronized (filterDefs) {
            FilterDef[] defs = new FilterDef[filterDefs.size()];
            return filterDefs.values().toArray(defs);
        }
    }

    @Override
    public void removeFilterDef(FilterDef filterDef) {
        synchronized (filterDefs) {
            filterDefs.remove(filterDef.getFilterName());
        }
    }

    @Override
    public boolean getAvailable() {
        return available;
    }

    @Override
    public void setAvailable(boolean available) {
        this.available = available;
    }

    @Override
    public boolean getConfigured() {
        return configured;
    }

    @Override
    public void setConfigured(boolean configured) {
        this.configured = configured;
    }


    //根据匹配路径返回Wrapper名称
    @Override
    public String findServletMapping(String pattern) {
        synchronized (servletMappings) {
            return servletMappings.get(pattern);
        }
    }

    //返回所有匹配路径
    @Override
    public String[] findServletMappings() {
        synchronized (servletMappings) {
            String[] paths = new String[servletMappings.size()];
            return servletMappings.keySet().toArray(paths);
        }
    }

    //添加匹配路径与servlet名之间的关系
    @Override
    public void addServletMapping(String pattern, String name) {
        //如果添加的映射中容器名不存在与context中,抛出异常
        if (findChild(name) == null)
            throw new IllegalArgumentException("Context容器中不存在名称为{" + name + "}的wrapper");
        //校正部分匹配路径格式
        pattern = adjustURLPattern(RequestUtil.URLDecode(pattern));
        //验证匹配路径是否正常
        if (!validateURLPattern(pattern))
            throw new IllegalArgumentException("Invalid <url-pattern> {" + pattern + "} in servlet mapping");
        //添加
        synchronized (servletMappings) {
            servletMappings.put(pattern, name);
        }
    }

    @Override
    public boolean getReloadable() {
        return this.reloadable;
    }

    @Override
    public void setReloadable(boolean reloadable) {
        this.reloadable = reloadable;
        if (loader != null)
            loader.setReloadable(reloadable);
    }


}
